package cc.rengu.igas.mcps.core.service.trans.common;


import cc.rengu.igas.channel.cups.common.constant.CupsConfigConstant;
import cc.rengu.igas.channel.cups.facade.base.Header;
import cc.rengu.igas.channel.cups.facade.bean.CupsTermInfo;
import cc.rengu.igas.channel.cups.facade.request.ScriptResultNoticeRequest;
import cc.rengu.igas.channel.cups.facade.response.ScriptResultNoticeResponse;
import cc.rengu.igas.channel.cups.facade.result.Result;
import cc.rengu.igas.mcps.common.constant.McpsParamConstant;
import cc.rengu.igas.mcps.common.constant.McpsTreeNodeConstant;
import cc.rengu.igas.mcps.common.dao.TermKeyMapper;
import cc.rengu.igas.mcps.common.dao.impl.TermKeyMapperImpl;
import cc.rengu.igas.mcps.common.entity.TermKey;
import cc.rengu.igas.mcps.common.enums.KeyTypeEnum;
import cc.rengu.igas.mcps.common.enums.RespCodeEnum;
import cc.rengu.igas.mcps.core.realize.PosParamDealService;
import cc.rengu.igas.mcps.core.realize.impl.PosParamDealServiceImpl;
import cc.rengu.igas.mcps.core.service.base.ManageService;
import cc.rengu.igas.mcps.facade.request.PosNoticeICScriptRequest;
import cc.rengu.igas.mcps.facade.response.PosNoticeICScriptResponse;
import cc.rengu.igas.share.core.model.MchntInfo;
import cc.rengu.igas.share.core.model.MchntTermInfo;
import cc.rengu.igas.share.core.realize.MchntService;
import cc.rengu.igas.share.core.realize.impl.MchntServiceImpl;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.FinanceTxnMapper;
import cc.rengu.oltp.service.common.dao.NonFinanceTxnMapper;
import cc.rengu.oltp.service.common.dao.PayOrderInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.FinanceTxnMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.NonFinanceTxnMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.PayOrderInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.FinanceTxn;
import cc.rengu.oltp.service.common.entity.NonFinanceTxn;
import cc.rengu.oltp.service.common.entity.PayOrderInfo;
import cc.rengu.oltp.service.common.enums.ChannelEnum;
import cc.rengu.oltp.service.common.enums.TransTypeEnum;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.model.BizResponse;
import cc.rengu.oltp.service.realize.DubboService;
import cc.rengu.oltp.service.realize.impl.DubboServiceImpl;
import cc.rengu.oltp.utility.util.*;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;

/**
 * POSC发起的IC卡脚本处理结果通知流程服务
 * Created by 张明 on 2018/4/24.
 */
public class PosNoticeICScriptService extends ManageService {

    @Override
    protected Object bizBeforeCust() throws Exception {
        PosNoticeICScriptRequest posNoticeICScriptReq = new PosNoticeICScriptRequest();
        ConvertUtil.convertOutput(posNoticeICScriptReq);
        return posNoticeICScriptReq;
    }

    @Override
    protected BizResponse bizProcessCust(Object request) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        BizResponse<PosNoticeICScriptResponse> bizResponse = new BizResponse<>();
        PosNoticeICScriptRequest posNoticeICScriptReq = (PosNoticeICScriptRequest) request;
        String oriTransNo = posNoticeICScriptReq.getOriTraceNo();
        String oriTransDate = posNoticeICScriptReq.getOriTransDate();
        String oriTransTime = posNoticeICScriptReq.getOriTransTime();
        String sysNum = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.SYS_SEQ_NUM);
        /*查询用户订单表，商户订单信息，支付订单信息表*/
        String instId = posNoticeICScriptReq.getHeader().getInstId();
        /*原系统标识*/
        String msgSrcId = posNoticeICScriptReq.getHeader().getSrcSysId();
        /*交易渠道标识*/
        String transChannelId = posNoticeICScriptReq.getHeader().getChanlId();

        PosParamDealService posParamDealService = new PosParamDealServiceImpl();
        String referenceNo = posParamDealService.getRefNo();
        /* 原交易时间 */
        PosNoticeICScriptResponse posNoticeICScriptResponse = new PosNoticeICScriptResponse();
        ConvertUtil.convertOutput(posNoticeICScriptResponse);
        posNoticeICScriptResponse.setRefNo(referenceNo);
        if (StringUtils.isEmpty(oriTransNo) || StringUtils.isEmpty(oriTransDate) || StringUtils.isEmpty(oriTransTime)) {
            rglog.error("传入原交易流水号为空，IC卡脚本处理结果通知流程服务结束");
            throw new BizException(RespCodeEnum.REQUEST_ERROR.getRespCode(), RespCodeEnum.REQUEST_ERROR.getRespDesc());
        }

        /* 获取原交易信息 */
        String origMchntNo = null;
        String origTermNo = null;
        String origPayAcctNo = null;
        String origTransStatus = null;
        String origCupsSsn = null;
        String origMsgType = null;
        String origProcessCode = null;
        String origTransDateTime = null;
        String origRespCode = null;
        FinanceTxnMapper financeTxnMapper = new FinanceTxnMapperImpl();
        FinanceTxn financeTxn = financeTxnMapper.selectFinanceTxnInfoByFrontSeqNum(instId, oriTransDate + oriTransTime, oriTransNo, msgSrcId, transChannelId);
        if (null != financeTxn) {
            origMchntNo = financeTxn.getMchntNo();
            origTermNo = financeTxn.getTermNo();
            origTransStatus = financeTxn.getTxnStatus();
            origCupsSsn = financeTxn.getSysSeqNum().substring(financeTxn.getSysSeqNum().length() - 6);
            origRespCode = financeTxn.getRespCode();
            PayOrderInfoMapper payOrderInfoMapper = new PayOrderInfoMapperImpl();
            List<PayOrderInfo> payOrderInfoList = payOrderInfoMapper.selectPayOrderInfoBySysSeqNum(instId, financeTxn.getSysSeqNum());
            if (null != payOrderInfoList && !payOrderInfoList.isEmpty()) {
                Optional<PayOrderInfo> payOrderInfoOpt = payOrderInfoList.stream().filter(item -> item.getMsgDstId().equals(ChannelEnum.CUPS.getChannelType())).findFirst();
                if (payOrderInfoOpt.isPresent()) {
                    origPayAcctNo = payOrderInfoOpt.get().getPayeeAcctInfo();
                    origTransDateTime = payOrderInfoOpt.get().getTxnDate().substring(4, 8) + payOrderInfoOpt.get().getTxnTime();
                }
            }
            if (TransTypeEnum.AUTH.equals(TransTypeEnum.getTransType(financeTxn.getTxnNum()))) {
                origMsgType = "0100"; //原交易为预授权
                origProcessCode = "030000";
            } else {
                origMsgType = "0200"; //原交易为消费
                origProcessCode = "000000";
            }
        } else {
            NonFinanceTxnMapper nonFinanceTxnMapper = new NonFinanceTxnMapperImpl();
            NonFinanceTxn nonFinanceTxn = nonFinanceTxnMapper.selectNonFinanceTxnByFrontSeqNum(instId, oriTransDate + oriTransTime, oriTransNo, msgSrcId, transChannelId);
            if (null != nonFinanceTxn) {
                origMchntNo = nonFinanceTxn.getMchntNo();
                origTermNo = nonFinanceTxn.getTermNo();
                origPayAcctNo = nonFinanceTxn.getPayAcctInfo();
                origTransStatus = nonFinanceTxn.getTxnStatus();
                origCupsSsn = nonFinanceTxn.getSysSeqNum().substring(nonFinanceTxn.getSysSeqNum().length() - 6);
                origTransDateTime = nonFinanceTxn.getTransDateTime().substring(4);
                origRespCode = nonFinanceTxn.getRespCode();
                origMsgType = "0200";//非金额类交易只为余额查询
                origProcessCode = "300000";
            }
        }
        rglog.debug("origMchntNo={},origTermNo={},origPayAcctNo={},origTransStatus={},origCupsSsn={},origMsgType={},origProcessCode={},origTransDateTime={},origRespCode={}",
                origMchntNo, origTermNo, origPayAcctNo, origTransStatus, origCupsSsn, origMsgType, origProcessCode, origTransDateTime, origRespCode);

        /* 校验原交易状态 */
        if (StringUtils.isEmpty(origTransStatus)) {
            rglog.error("找不到原交易，不可上送脚本处理结果通知!frontSeqNum:<{}>", oriTransNo);
            throw new BizException(RespCodeEnum.POS_ORIG_STATUS_FAIL.getRespCode(), RespCodeEnum.POS_ORIG_STATUS_FAIL.getRespDesc());
        }

        /* 判断卡号 */
        if (posNoticeICScriptReq.getPayAcctNo() != null) {
            /* 敏感信息解密,获取终端工作密钥*/
            TermKeyMapper termKeyMapper = new TermKeyMapperImpl();
            TermKey tdkTermkey = termKeyMapper.getTermKey(instId, posNoticeICScriptReq.getMchntNo(), posNoticeICScriptReq.getTermNo(), KeyTypeEnum.TDK.getTypeCode());
            if (tdkTermkey == null) {
                rglog.error("未获取到敏感信息加密密钥，拒绝交易");
                throw new BizException(RespCodeEnum.MCHNT_APP_SECRTT_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_APP_SECRTT_NOT_FOUND.getRespDesc());
            }
            xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.TDK_KEY, tdkTermkey.getKeyValue());

            String payAcctNo = descriptData(tdkTermkey, posNoticeICScriptReq.getPayAcctNo());
            if (payAcctNo == null || payAcctNo.length() > 36) {
                rglog.error("解密数据失败，拒绝交易");
                xmlTreeUtil.setXmlTreeStringValue(McpsTreeNodeConstant.PAN, "");
                throw new BizException(RespCodeEnum.MCHNT_APP_SECRTT_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_APP_SECRTT_NOT_FOUND.getRespDesc());
            }
            posNoticeICScriptReq.setPayAcctNo(payAcctNo);
        }

        /* 判断终端号 */
        if (!posNoticeICScriptReq.getTermNo().trim().equals(origTermNo)) {
            rglog.error("检查原交易信息失败,终端号不一致 orgTermId:<{}>. ", origTermNo);
            throw new BizException(RespCodeEnum.POS_INVALID_REL_TXN.getRespCode(), RespCodeEnum.POS_INVALID_REL_TXN.getRespDesc());
        }

        /* 判断商户号 */
        if (!posNoticeICScriptReq.getMchntNo().equals(origMchntNo)) {
            rglog.error("检查原交易信息失败,商户号不一致 orgMchntNo:<{}>. ", origMchntNo);
            throw new BizException(RespCodeEnum.POS_INVALID_REL_TXN.getRespCode(), RespCodeEnum.POS_INVALID_REL_TXN.getRespDesc());
        }

        //异步调用银联通知接口
        Result<ScriptResultNoticeResponse> result = cupsNoticeICScript(posNoticeICScriptReq, origMsgType, origProcessCode, origCupsSsn, origTransDateTime, origRespCode, referenceNo);
        /*通知失败处理*/
        if (null != result && null != result.getResult()) {
            if (result.getResult().getRespCode().equals(McpsParamConstant.DUBBO_SUCCESS)) {
                bizResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
                bizResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespCode());
            } else if (result.getResult().getRespCode().equals(McpsParamConstant.DUBBO_OVERTIME) ||
                    result.getResult().getRespCode().equals(McpsParamConstant.COMMUNICATION_OVERTIME)) {
                bizResponse.setRespCode(RespCodeEnum.POS_RSP_TIME_OUT.getRespCode());
                bizResponse.setRespDesc(RespCodeEnum.POS_RSP_TIME_OUT.getRespCode());
            } else {
                bizResponse.setRespCode(RespCodeEnum.TRANS_FAIL.getRespCode());
                bizResponse.setRespDesc(RespCodeEnum.TRANS_FAIL.getRespDesc());
            }
        } else {
            bizResponse.setRespCode(RespCodeEnum.TRANS_FAIL.getRespCode());
            bizResponse.setRespDesc(RespCodeEnum.TRANS_FAIL.getRespDesc());
        }

        /* 重组响应结果 */
        posNoticeICScriptResponse.setPayId(sysNum);
        bizResponse.setRspSysId(AppParamConstant.IGAS);
        bizResponse.setResult(posNoticeICScriptResponse);

        return bizResponse;
    }

    @Override
    protected void bizAfterCust(Object response) throws Exception {
        BizResponse bizResponse = (BizResponse) response;
        PosNoticeICScriptResponse posNoticeICScriptResponse = (PosNoticeICScriptResponse) bizResponse.getResult();
        ConvertUtil.convertInput(posNoticeICScriptResponse);
    }

    /*
     * 调用银联
     * */
    private Result<ScriptResultNoticeResponse> cupsNoticeICScript(PosNoticeICScriptRequest posNoticeICScriptRequest,
                                                                  String origMsgType, String origProcessCode,
                                                                  String origCupsSsn, String origTransDateTime,
                                                                  String origRespCode, String refNo) throws Exception {
        DubboService dubboService = new DubboServiceImpl();
        PosParamDealService posParamDealService = new PosParamDealServiceImpl();
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = posNoticeICScriptRequest.getHeader().getInstId();
        String sysNum = xmlTreeUtil.getXmlTreeStringValue(McpsTreeNodeConstant.SYS_SEQ_NUM);
        /*获取商户基本信息*/
        MchntService mchntService = new MchntServiceImpl();
        MchntInfo mchntInfo = mchntService.getMchntInfo(instId, posNoticeICScriptRequest.getMchntNo());
        if (null == mchntInfo) {
            rglog.error("获取商户信息失败");
            throw new BizException(RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespCode(), RespCodeEnum.MCHNT_INFO_NOT_FOUND.getRespDesc());
        }

        /*准备请求报文*/
        ScriptResultNoticeRequest scriptResultNoticeRequest = new ScriptResultNoticeRequest();
        /*设置请求报文*/
        Header header = new Header();
        BeanUtil.beanCopy(posNoticeICScriptRequest.getHeader(), header);
        header.setVersion("1.0.0");
        /*通道交易码*/
        String acqInstCode = posParamDealService.genCupsAcqInstCode(instId, posNoticeICScriptRequest.getMchntNo());
        String fwdInstCode = XmlConfigUtil.getXmlConfigCacheKeyValue(CupsConfigConstant.CUPS_CONF_NODE, CupsConfigConstant.CUPS_FWD_INS_CODE);

        header.setTxnNum("06209510");
        header.setTraceNo(sysNum);
        scriptResultNoticeRequest.setHeader(header);
        scriptResultNoticeRequest.setCupsTraceNo(sysNum.substring(sysNum.length() - 6));
        String transDate = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_DATE);
        String transTime = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.TRANS_TIME);
        scriptResultNoticeRequest.setTransDateTime(transDate.substring(4, 8) + transTime);
        scriptResultNoticeRequest.setMchntNo(mchntInfo.getCupsMchntNo());
        scriptResultNoticeRequest.setTermNo(posNoticeICScriptRequest.getTermNo());
        scriptResultNoticeRequest.setMchntShortName(mchntInfo.getMchntSimpleName());
        scriptResultNoticeRequest.setMchntType(mchntInfo.getIndustryType());
        scriptResultNoticeRequest.setAcqInstCode(acqInstCode);
        scriptResultNoticeRequest.setFwdInstCode(fwdInstCode);
        scriptResultNoticeRequest.setEntryMode(posNoticeICScriptRequest.getEntryMode());
        scriptResultNoticeRequest.setOrderAmt(posNoticeICScriptRequest.getTransAmt());
        scriptResultNoticeRequest.setOrderCcy(McpsParamConstant.INNER_DEFAULT_ORDERCCY);
        scriptResultNoticeRequest.setPayAcctNo(posNoticeICScriptRequest.getPayAcctNo());
        scriptResultNoticeRequest.setReferenceNo(refNo);
        scriptResultNoticeRequest.setIcData(posNoticeICScriptRequest.getIcData());
        scriptResultNoticeRequest.setCardSeqNo(posNoticeICScriptRequest.getCardSeqNo());
        scriptResultNoticeRequest.setTermRead(posNoticeICScriptRequest.getTermRead());
        scriptResultNoticeRequest.setIcConCode(posNoticeICScriptRequest.getIcConCode());
        if (StringUtils.isEmpty(posNoticeICScriptRequest.getTermInfo().getDeviceType())) {
            MchntTermInfo mchntTermInfo = mchntService.getMchntTermInfo(instId, posNoticeICScriptRequest.getMchntNo(), posNoticeICScriptRequest.getTermNo());
            scriptResultNoticeRequest.setTermType(mchntTermInfo.getTermType());
        } else {
            scriptResultNoticeRequest.setTermType(posNoticeICScriptRequest.getTermInfo().getDeviceType());
        }

        scriptResultNoticeRequest.setOrigCupsSsn(origCupsSsn);
        scriptResultNoticeRequest.setOrigTransDateTime(origTransDateTime);
        scriptResultNoticeRequest.setOrigMsgType(origMsgType);
        scriptResultNoticeRequest.setOrigRespCode(origRespCode);
        scriptResultNoticeRequest.setProcCode(origProcessCode);
        /*设置终端信息*/
        CupsTermInfo cupsTermInfo = new CupsTermInfo();
        BeanUtil.beanCopy(posNoticeICScriptRequest.getTermInfo(), cupsTermInfo);
        Result<ScriptResultNoticeResponse> result = (Result<ScriptResultNoticeResponse>) dubboService.callDubboService("cupsPayService", "scriptResultNotice", scriptResultNoticeRequest);
        rglog.debug("dubbo调用银行卡交易结果通知结果：{}", JSON.toJSONString(result));
        return result;
    }

    String descriptData(TermKey termKey, String encData) {
        String algorithmType = termKey.getAlgorithmType();
        rglog.debug("使用软加密,算法类型:<{}>,", algorithmType);
        if ("3DES".equals(algorithmType)) {
            byte[] descData = DES3Util.decryptMode3DESDouble(ByteUtil.hexStringToByte(termKey.getKeyValue()), Base64.decodeBase64(encData));
            if (descData != null) {
                return new String(descData);
            }
        } else {
            byte[] bEncData = Base64.decodeBase64(encData);
            byte[] descData = new byte[bEncData.length];
            int retCode = SM4Util.sms4(bEncData, bEncData.length, ByteUtil.hexStringToByte(termKey.getKeyValue()), descData, 0);
            if (retCode == 0) {
                for (int i = 0; i < descData.length; i++) {
                    if (descData[i] == 0) {
                        return new String(descData, 0, i);
                    }
                }
                return new String(descData);
            }
        }
        return null;
    }
}
